#include "bmpView_JY.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// #include <cstddef>

#ifdef SHOW_BMPVIEW_LOG
#define LOG(...)                          \
	do                                    \
	{                                     \
		if (SHOW_BMPVIEW_LOG)             \
			fprintf(stderr, __VA_ARGS__); \
	} while (0)
#else
#define LOG(...) \
	do           \
	{            \
	} while (0)
#endif // SHOW_BMPVIEW_LOG

// typedef struct tagBITMAPFILEHEADER {
//	unsigned short	bfType;				//   0~ 1   identity : "BM"
//	unsigned long	bfSize;				//	 2~ 5	file size
//	unsigned short	bfReserved1;		//	 6  7	reserved1
//	unsigned short	bfReserved2;		//	 8  9	reserved2
//	unsigned long	bfOffBits;			//	10~13	RGB data offset
// } BITMAPFILEHEADER;
// typedef struct tagBITMAPINFOHEADER {
//	unsigned long	biSize;				//	14~17	struct BITMAPINFOHEADER size
//	long			biWidth;			//	18~21	bmp width
//	long			biHeight;			//	22~25	bmp height
//	unsigned short	biPlanes;			//  26 27	planes
//	unsigned short	biBitCount;			//	28 29	bit per pixel
//	unsigned long	biCompression;		//	30~33	compression
//	unsigned long	biSizeImage;		//	34~37	data size
//	long			biXPelsPerMeter;	//	38~41	h resolution
//	long			biYPelsPerMeter;	//	42~45	v resolution
//	unsigned long	biClrUsed;			//	46~49	used colors
//	unsigned long	biClrImportant;		//	50~53	important colors
// } BITMAPINFOHEADER;
// typedef struct tagRGBQUAD {
//	unsigned char	rgbBlue;
//	unsigned char	rgbGreen;
//	unsigned char	rgbRed;
//	unsigned char	rgbReserved;
// } RGBQUAD;

typedef struct _BITMAPFILEHEADER
{
	uint16_t bfType;	  //   0~ 1   identity : "BM"
	uint32_t bfSize;	  //	 2~ 5	file size
	uint16_t bfReserved1; //	 6  7	reserved1
	uint16_t bfReserved2; //	 8  9	reserved2
	uint32_t bfOffBits;	  //	10~13	RGB data offset
} BITMAPFILEHEADER;
typedef struct _BITMAPINFOHEADER
{
	uint32_t biSize;		 //	14~17	struct BITMAPINFOHEADER size
	int32_t biWidth;		 //	18~21	bmp width
	int32_t biHeight;		 //	22~25	bmp height
	uint16_t biPlanes;		 //  26 27	planes
	uint16_t biBitCount;	 //	28 29	bit per pixel
	uint32_t biCompression;	 //	30~33	compression
	uint32_t biSizeImage;	 //	34~37	data size
	int32_t biXPelsPerMeter; //	38~41	h resolution
	int32_t biYPelsPerMeter; //	42~45	v resolution
	uint32_t biClrUsed;		 //	46~49	used colors
	uint32_t biClrImportant; //	50~53	important colors
} BITMAPINFOHEADER;
typedef struct _RGBQUAD
{
	uint8_t rgbBlue;
	uint8_t rgbGreen;
	uint8_t rgbRed;
	uint8_t rgbReserved;
} RGBQUAD;

typedef struct _BMP
{
	// header 14byte 0~13
	BITMAPFILEHEADER fileHeader;
	// info 40byte 14~53
	BITMAPINFOHEADER infoHeader;
	// color palette
	RGBQUAD *colorPalette;
	// bmp data , always BGR
	uint8_t *data;

} BMP;

BMP new_BMP()
{
	BMP bmp;
	bmp.colorPalette = (RGBQUAD *)nullptr;
	bmp.data = (uint8_t *)nullptr;

	return bmp;
}

void BMP_init(BMP *bmp, int width, int height)
{
	bmp->fileHeader.bfType = 0x4D42;				  //   0~ 1   identity : "BM"
	bmp->fileHeader.bfSize = 54 + width * height * 3; //	 2~ 5	file size
	bmp->fileHeader.bfReserved1 = 0;				  //	 6  7	reserved1
	bmp->fileHeader.bfReserved2 = 0;				  //	 8  9	reserved2
	bmp->fileHeader.bfOffBits = 54;					  //	10~13	RGB data offset

	bmp->infoHeader.biSize = 40;					  //	14~17	struct BITMAPINFOHEADER size
	bmp->infoHeader.biWidth = width;				  //	18~21	bmp width
	bmp->infoHeader.biHeight = height;				  //	22~25	bmp height
	bmp->infoHeader.biPlanes = 1;					  //  26 27	planes
	bmp->infoHeader.biBitCount = 24;				  //	28 29	bit per pixel
	bmp->infoHeader.biCompression = 0;				  //	30~33	compression
	bmp->infoHeader.biSizeImage = width * height * 3; //	34~37	data size
	bmp->infoHeader.biXPelsPerMeter = 0;			  //	38~41	h resolution
	bmp->infoHeader.biYPelsPerMeter = 0;			  //	42~45	v resolution
	bmp->infoHeader.biClrUsed = 0;					  //	46~49	used colors
	bmp->infoHeader.biClrImportant = 0;				  //	50~53	important colors

	// init & process data
	bmp->data = (uint8_t *)malloc(bmp->infoHeader.biSizeImage * sizeof(uint8_t));
	memset(bmp->data, 0, bmp->infoHeader.biSizeImage * sizeof(uint8_t));
}

int BMP_read(BMP *bmp, const char *file_name)
{
	////show type size
	// if (SHOW_BMPVIEW_LOG) {
	//	printf("\n");
	//	printf("sizeof(uint8_t): %lu\n", sizeof(uint8_t));
	//	printf("sizeof(uint16_t): %lu\n", sizeof(uint16_t));
	//	printf("sizeof(uint32_t): %lu\n", sizeof(uint32_t));
	//	printf("sizeof(int32_t): %lu\n", sizeof(int32_t));
	//	printf("\n");
	//	printf("sizeof(unsigned char): %lu\n", sizeof(unsigned char));
	//	printf("sizeof(unsigned short): %lu\n", sizeof(unsigned short));
	//	printf("sizeof(unsigned long): %lu\n", sizeof(unsigned long));
	//	printf("sizeof(long): %lu\n", sizeof(long));
	//	printf("\n");
	//	printf("sizeof(BITMAPFILEHEADER): %lu\n", sizeof(BITMAPFILEHEADER));
	//	printf("sizeof(fileHeader): %lu\n", sizeof(fileHeader));
	//	printf("sizeof(fileHeader.bfType): %lu\n", sizeof(fileHeader.bfType));
	//	printf("sizeof(fileHeader.bfSize): %lu\n", sizeof(fileHeader.bfSize));
	//	printf("sizeof(fileHeader.bfReserved1): %lu\n", sizeof(fileHeader.bfReserved1));
	//	printf("sizeof(fileHeader.bfReserved2): %lu\n", sizeof(fileHeader.bfReserved2));
	//	printf("sizeof(fileHeader.bfOffBits): %lu\n", sizeof(fileHeader.bfOffBits));
	//	printf("sizeof(BITMAPINFOHEADER): %lu\n", sizeof(BITMAPINFOHEADER));
	//	printf("sizeof(RGBQUAD): %lu\n", sizeof(RGBQUAD));
	//	printf("sizeof(uint8_t): %lu\n", sizeof(uint8_t));
	//	printf("\n");
	// }

	FILE *fp_s = NULL;
	int res_fread;
	// if (fopen_s(&fp_s, file_name, "rb") != 0)
	fp_s = fopen(file_name, "rb");
	if (fp_s == NULL)
	{
		printf("Error opening file!\n");
		return BMPVIEW_FAILED_OPEN_FILE;
	}

	// read file header
	fseek(fp_s, 0, SEEK_SET);
	////fread(&fileHeader, BITMAPFILEHEADER, 1, fp_s);
	res_fread = fread(&(*bmp).fileHeader.bfType, sizeof((*bmp).fileHeader.bfType), 1, fp_s);
	res_fread = fread(&(*bmp).fileHeader.bfSize, sizeof((*bmp).fileHeader.bfSize), 1, fp_s);
	res_fread = fread(&(*bmp).fileHeader.bfReserved1, sizeof((*bmp).fileHeader.bfReserved1), 1, fp_s);
	res_fread = fread(&(*bmp).fileHeader.bfReserved2, sizeof((*bmp).fileHeader.bfReserved2), 1, fp_s);
	res_fread = fread(&(*bmp).fileHeader.bfOffBits, sizeof((*bmp).fileHeader.bfOffBits), 1, fp_s);

	LOG("bfType: %x\n", (*bmp).fileHeader.bfType);
	LOG("bfSize: %d\n", (int)(*bmp).fileHeader.bfSize);
	LOG("bfReserved1: %d\n", (int)(*bmp).fileHeader.bfReserved1);
	LOG("bfReserved2: %d\n", (int)(*bmp).fileHeader.bfReserved2);
	LOG("bfOffBits: %d\n", (int)(*bmp).fileHeader.bfOffBits);

	// read info header
	fseek(fp_s, 14, SEEK_SET);
	////fread(&infoHeader, BITMAPINFOHEADER, 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biSize, sizeof((*bmp).infoHeader.biSize), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biWidth, sizeof((*bmp).infoHeader.biWidth), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biHeight, sizeof((*bmp).infoHeader.biHeight), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biPlanes, sizeof((*bmp).infoHeader.biPlanes), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biBitCount, sizeof((*bmp).infoHeader.biBitCount), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biCompression, sizeof((*bmp).infoHeader.biCompression), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biSizeImage, sizeof((*bmp).infoHeader.biSizeImage), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biXPelsPerMeter, sizeof((*bmp).infoHeader.biXPelsPerMeter), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biYPelsPerMeter, sizeof((*bmp).infoHeader.biYPelsPerMeter), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biClrUsed, sizeof((*bmp).infoHeader.biClrUsed), 1, fp_s);
	res_fread = fread(&(*bmp).infoHeader.biClrImportant, sizeof((*bmp).infoHeader.biClrImportant), 1, fp_s);

	LOG("biSize: %d\n", (int)(*bmp).infoHeader.biSize);
	LOG("biWidth: %d\n", (int)(*bmp).infoHeader.biWidth);
	LOG("biHeight: %d\n", (int)(*bmp).infoHeader.biHeight);
	LOG("biPlanes: %d\n", (int)(*bmp).infoHeader.biPlanes);
	LOG("biBitCount: %d\n", (int)(*bmp).infoHeader.biBitCount);
	LOG("biCompression: %d\n", (int)(*bmp).infoHeader.biCompression);
	LOG("biSizeImage: %d\n", (int)(*bmp).infoHeader.biSizeImage);
	LOG("biXPelsPerMeter: %d\n", (int)(*bmp).infoHeader.biXPelsPerMeter);
	LOG("biYPelsPerMeter: %d\n", (int)(*bmp).infoHeader.biYPelsPerMeter);
	LOG("biClrUsed: %d\n", (int)(*bmp).infoHeader.biClrUsed);
	LOG("biClrImportant: %d\n", (int)(*bmp).infoHeader.biClrImportant);

	// read color Palette
	size_t colorPalette_size = 0;
	if ((*bmp).infoHeader.biBitCount == 8)
	{
		colorPalette_size = 256 * sizeof(RGBQUAD); // 256=2^8
		(*bmp).colorPalette = (RGBQUAD *)malloc(colorPalette_size);
		LOG("colorPalette_size: %d\n", (int)colorPalette_size);
	}
	else if ((*bmp).infoHeader.biBitCount == 24)
	{
		(*bmp).colorPalette = (RGBQUAD *)nullptr;
	}
	else if ((*bmp).infoHeader.biBitCount == 32)
	{
		(*bmp).colorPalette = (RGBQUAD *)nullptr;
	}
	else
	{
		printf("Color Palette is not suported!!\n");
		fclose(fp_s);
		return BMPVIEW_COLOR_PALETTTE_ERROR;
	}

	if ((*bmp).colorPalette != nullptr)
	{
		fseek(fp_s, 54, SEEK_SET);
		// fread((*bmp).colorPalette, colorPalette_size, 1, fp_s);
		for (int i = 0; i < (int)colorPalette_size; i += 4)
		{
			res_fread = fread(&(*bmp).colorPalette[i].rgbBlue, 1, 1, fp_s);
			res_fread = fread(&(*bmp).colorPalette[i].rgbGreen, 1, 1, fp_s);
			res_fread = fread(&(*bmp).colorPalette[i].rgbRed, 1, 1, fp_s);
			res_fread = fread(&(*bmp).colorPalette[i].rgbReserved, 1, 1, fp_s);
		}
	}
	// read data
	fseek(fp_s, 54 + colorPalette_size, SEEK_SET);
	(*bmp).data = (uint8_t *)malloc((*bmp).infoHeader.biWidth * (*bmp).infoHeader.biHeight * ((*bmp).infoHeader.biBitCount / 8) * sizeof(uint8_t));
	if ((*bmp).data == NULL)
	{
		printf("malloc (*bmp).data error\n");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}
	res_fread = fread((*bmp).data, sizeof(uint8_t), (*bmp).infoHeader.biWidth * (*bmp).infoHeader.biHeight * ((*bmp).infoHeader.biBitCount / 8), fp_s);

	if (res_fread)
	{
		// todo
	}

	fclose(fp_s);
	return BMPVIEW_SUCESS;
}

int BMP_save(BMP *bmp, const char *file_name)
{

	FILE *fp_t = NULL;
	// if (fopen_s(&fp_t, file_name, "wb") != 0)
	fp_t = fopen(file_name, "wb");
	if (fp_t == NULL)
	{
		printf("Error opening file!\n");
		return BMPVIEW_FAILED_OPEN_FILE;
	}

	// write fileHeader
	fseek(fp_t, 0, SEEK_SET);
	////fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fp_t);
	fwrite(&bmp->fileHeader.bfType, sizeof(bmp->fileHeader.bfType), 1, fp_t);
	fwrite(&bmp->fileHeader.bfSize, sizeof(bmp->fileHeader.bfSize), 1, fp_t);
	fwrite(&bmp->fileHeader.bfReserved1, sizeof(bmp->fileHeader.bfReserved1), 1, fp_t);
	fwrite(&bmp->fileHeader.bfReserved2, sizeof(bmp->fileHeader.bfReserved2), 1, fp_t);
	fwrite(&bmp->fileHeader.bfOffBits, sizeof(bmp->fileHeader.bfOffBits), 1, fp_t);

	// write infoHeader
	fseek(fp_t, 14, SEEK_SET);
	////fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fp_t);
	fwrite(&bmp->infoHeader.biSize, sizeof(bmp->infoHeader.biSize), 1, fp_t);
	fwrite(&bmp->infoHeader.biWidth, sizeof(bmp->infoHeader.biWidth), 1, fp_t);
	fwrite(&bmp->infoHeader.biHeight, sizeof(bmp->infoHeader.biHeight), 1, fp_t);
	fwrite(&bmp->infoHeader.biPlanes, sizeof(bmp->infoHeader.biPlanes), 1, fp_t);
	fwrite(&bmp->infoHeader.biBitCount, sizeof(bmp->infoHeader.biBitCount), 1, fp_t);
	fwrite(&bmp->infoHeader.biCompression, sizeof(bmp->infoHeader.biCompression), 1, fp_t);
	fwrite(&bmp->infoHeader.biSizeImage, sizeof(bmp->infoHeader.biSizeImage), 1, fp_t);
	fwrite(&bmp->infoHeader.biXPelsPerMeter, sizeof(bmp->infoHeader.biXPelsPerMeter), 1, fp_t);
	fwrite(&bmp->infoHeader.biYPelsPerMeter, sizeof(bmp->infoHeader.biYPelsPerMeter), 1, fp_t);
	fwrite(&bmp->infoHeader.biClrUsed, sizeof(bmp->infoHeader.biClrUsed), 1, fp_t);
	fwrite(&bmp->infoHeader.biClrImportant, sizeof(bmp->infoHeader.biClrImportant), 1, fp_t);

	// write image
	fwrite(bmp->data, sizeof(uint8_t), bmp->infoHeader.biSizeImage, fp_t);

	fclose(fp_t);
	return BMPVIEW_SUCESS;
}

void BMP_resize(BMP *bmp, int width, int height)
{
	uint8_t *newImage = (uint8_t *)malloc(width * height * 3 * sizeof(uint8_t));

	double scaleWidth = (double)width / (double)bmp->infoHeader.biWidth;
	double scaleHeight = (double)height / (double)bmp->infoHeader.biHeight;

	int x, y, ch;
	int pixel, nearestMatch;
	for (y = 0; y < height; y++)
	{
		for (x = 0; x < width; x++)
		{
			for (ch = 0; ch < 3; ch++)
			{
				pixel = (y * width + x) * 3 + ch;
				nearestMatch = (((int)(y / scaleHeight) * bmp->infoHeader.biWidth) + (int)(x / scaleWidth)) * 3 + ch;

				*(newImage + pixel) = *(bmp->data + nearestMatch);
			}
		}
	}

	free(bmp->data);
	bmp->data = (uint8_t *)malloc(width * height * 3 * sizeof(uint8_t));
	memcpy(bmp->data, newImage, width * height * 3 * sizeof(uint8_t));

	bmp->infoHeader.biWidth = width;
	bmp->infoHeader.biHeight = height;

	free(bmp->colorPalette);
	bmp->colorPalette = (RGBQUAD *)nullptr;
	free(newImage);
}

int imageInit(void *arr, int width, int height, int channel)
{
	// printf("%s\n", typeid(**(BYTE**)arr).name());

	*(uint8_t **)arr = (uint8_t *)malloc(width * height * channel * sizeof(uint8_t));
	if (*(uint8_t **)arr == NULL)
	{
		printf("imageInit malloc Error!!\n");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}

	arr = nullptr;

	return BMPVIEW_SUCESS;
}

int BGR_to_RGB(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	uint8_t B, G, R;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			B = *(inStream + ((y * width + x) * 3) + 0);
			G = *(inStream + ((y * width + x) * 3) + 1);
			R = *(inStream + ((y * width + x) * 3) + 2);

			*(outStream + (width * y + x) * 3 + 0) = R;
			*(outStream + (width * y + x) * 3 + 1) = G;
			*(outStream + (width * y + x) * 3 + 2) = B;
		}
	}

	return 0;
}

int BGR_to_GRAY(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	uint8_t B, G, R, GRAY;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			B = *(inStream + ((y * width + x) * 3) + 0);
			G = *(inStream + ((y * width + x) * 3) + 1);
			R = *(inStream + ((y * width + x) * 3) + 2);
			GRAY = (R + G + B) / 3;

			*(outStream + (width * y + x)) = GRAY;
		}
	}

	return 0;
}

int BGR_to_RGBA(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	uint8_t B, G, R;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			B = *(inStream + ((y * width + x) * 3) + 0);
			G = *(inStream + ((y * width + x) * 3) + 1);
			R = *(inStream + ((y * width + x) * 3) + 2);

			*(outStream + (width * y + x) * 4 + 0) = R;
			*(outStream + (width * y + x) * 4 + 1) = G;
			*(outStream + (width * y + x) * 4 + 2) = B;
		}
	}

	return 0;
}

int BGR_to_BGRA(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	uint8_t B, G, R;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			B = *(inStream + ((y * width + x) * 3) + 0);
			G = *(inStream + ((y * width + x) * 3) + 1);
			R = *(inStream + ((y * width + x) * 3) + 2);

			*(outStream + (width * y + x) * 4 + 0) = B;
			*(outStream + (width * y + x) * 4 + 1) = G;
			*(outStream + (width * y + x) * 4 + 2) = R;
		}
	}

	return 0;
}

int BGR_to_ARGB(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	uint8_t B, G, R;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			B = *(inStream + ((y * width + x) * 3) + 0);
			G = *(inStream + ((y * width + x) * 3) + 1);
			R = *(inStream + ((y * width + x) * 3) + 2);

			*(outStream + (width * y + x) * 4 + 0) = 0;
			*(outStream + (width * y + x) * 4 + 1) = R;
			*(outStream + (width * y + x) * 4 + 2) = G;
			*(outStream + (width * y + x) * 4 + 3) = B;
		}
	}

	return 0;
}

int BGR_to_ABGR(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	uint8_t B, G, R;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			B = *(inStream + ((y * width + x) * 3) + 0);
			G = *(inStream + ((y * width + x) * 3) + 1);
			R = *(inStream + ((y * width + x) * 3) + 2);

			*(outStream + (width * y + x) * 4 + 0) = 0;
			*(outStream + (width * y + x) * 4 + 1) = B;
			*(outStream + (width * y + x) * 4 + 2) = G;
			*(outStream + (width * y + x) * 4 + 3) = R;
		}
	}

	return 0;
}

int RGB_to_BGR(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	BGR_to_RGB(outStream, inStream, width, height);
	return 0;
}

int RGBA_to_BGR(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	uint8_t B, G, R;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			R = *(inStream + ((y * width + x) * 4) + 0);
			G = *(inStream + ((y * width + x) * 4) + 1);
			B = *(inStream + ((y * width + x) * 4) + 2);

			*(outStream + (width * y + x) * 3 + 0) = B;
			*(outStream + (width * y + x) * 3 + 1) = G;
			*(outStream + (width * y + x) * 3 + 2) = R;
		}
	}

	return 0;
}

int BGRA_to_BGR(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	uint8_t B, G, R;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			B = *(inStream + ((y * width + x) * 4) + 0);
			G = *(inStream + ((y * width + x) * 4) + 1);
			R = *(inStream + ((y * width + x) * 4) + 2);

			*(outStream + (width * y + x) * 3 + 0) = B;
			*(outStream + (width * y + x) * 3 + 1) = G;
			*(outStream + (width * y + x) * 3 + 2) = R;
		}
	}

	return 0;
}

int GRAY_to_BGR(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			*(outStream + ((y * width + x) * 3) + 0) = *(inStream + (y * width + x));
			*(outStream + ((y * width + x) * 3) + 1) = *(inStream + (y * width + x));
			*(outStream + ((y * width + x) * 3) + 2) = *(inStream + (y * width + x));
		}
	}
	return 0;
}

int GrayR_to_BGR(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			*(outStream + ((y * width + x) * 3) + 0) = 0;
			*(outStream + ((y * width + x) * 3) + 1) = 0;
			*(outStream + ((y * width + x) * 3) + 2) = *(inStream + (y * width + x));
		}
	}
	return 0;
}

int GrayG_to_BGR(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			*(outStream + ((y * width + x) * 3) + 0) = 0;
			*(outStream + ((y * width + x) * 3) + 1) = *(inStream + (y * width + x));
			*(outStream + ((y * width + x) * 3) + 2) = 0;
		}
	}
	return 0;
}

int GrayB_to_BGR(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			*(outStream + ((y * width + x) * 3) + 0) = *(inStream + (y * width + x));
			*(outStream + ((y * width + x) * 3) + 1) = 0;
			*(outStream + ((y * width + x) * 3) + 2) = 0;
		}
	}
	return 0;
}

int BMP_to_Stream(uint8_t *dataStream, BMP *bmp, ColorTpye readTo)
{
	int width = bmp->infoHeader.biWidth;
	int height = bmp->infoHeader.biHeight;
	uint8_t B, G, R;

	uint8_t *tmpImage;
	imageInit(&tmpImage, width, height, 3);

	if (bmp->colorPalette == nullptr)
	{
		// 24 bit
		if (bmp->infoHeader.biBitCount == 24)
		{
			memcpy(tmpImage, bmp->data, width * height * 3);
		}
		// 32 bit
		else if (bmp->infoHeader.biBitCount == 32)
		{
			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					B = *(bmp->data + (y * width + x) * 4);
					G = *(bmp->data + (y * width + x) * 4 + 1);
					R = *(bmp->data + (y * width + x) * 4 + 2);
					// A = *(bmp->data + (y * width + x) * 4 + 3);  // not use

					*(tmpImage + (width * y + x) * 3 + 0) = B;
					*(tmpImage + (width * y + x) * 3 + 1) = G;
					*(tmpImage + (width * y + x) * 3 + 2) = R;
				}
			}
		}
	}
	// color Palette
	else
	{
		uint8_t color_num;
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				color_num = *(bmp->data + (y * width + x));

				B = (bmp->colorPalette + color_num)->rgbBlue;
				G = (bmp->colorPalette + color_num)->rgbGreen;
				R = (bmp->colorPalette + color_num)->rgbRed;

				*(tmpImage + (width * y + x) * 3 + 0) = B;
				*(tmpImage + (width * y + x) * 3 + 1) = G;
				*(tmpImage + (width * y + x) * 3 + 2) = R;
			}
		}
	}

	// Write Stream
	switch (readTo)
	{
	case GRAY:
		BGR_to_GRAY(dataStream, tmpImage, width, height);
		break;
	case RGB:
		BGR_to_RGB(dataStream, tmpImage, width, height);
		break;
	case BGR:
		memcpy(dataStream, tmpImage, width * height * 3 * sizeof(uint8_t));
		break;
	case RGBA:
		BGR_to_RGBA(dataStream, tmpImage, width, height);
		break;
	case BGRA:
		BGR_to_BGRA(dataStream, tmpImage, width, height);
		break;
	case ARGB:
		BGR_to_ARGB(dataStream, tmpImage, width, height);
		break;
	case ABGR:
		BGR_to_ABGR(dataStream, tmpImage, width, height);
		break;
	default:
		// TODO
		printf("UNIMPLEMENTED!!");
		break;
	}

	free(tmpImage);

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg readBMP_JY(uint8_t **dstImage, int *dstWidth, int *dstHeight, const char *file_name, ColorTpye readTo, bool isFlipped)
{
	// Read Bitmap
	BMP bmp = new_BMP();
	// bmp.read(file_name);
	BMP_read(&bmp, file_name);

	// save W H
	*dstWidth = bmp.infoHeader.biWidth;
	*dstHeight = bmp.infoHeader.biHeight;
	LOG("dstWidth: %d\n", (int)bmp.infoHeader.biWidth);
	LOG("dstHeight: %d\n", (int)bmp.infoHeader.biHeight);

	// read colorPalette
	if (bmp.colorPalette != nullptr)
	{ // 8 bits
		uint8_t *newData = (uint8_t *)malloc(bmp.infoHeader.biWidth * bmp.infoHeader.biHeight * 3 * sizeof(uint8_t));

		//// print colorPalette
		// for (int i = 0; i < 256; i++) {
		//	printf("%d %d %d %d\n", bmp.colorPalette[i * 4].rgbBlue, bmp.colorPalette[i * 4].rgbGreen, bmp.colorPalette[i * 4].rgbRed, bmp.colorPalette[i * 4].rgbReserved);
		// }

		for (int i = 0; i < bmp.infoHeader.biWidth * bmp.infoHeader.biHeight; i++)
		{
			newData[i * 3 + 0] = bmp.colorPalette[bmp.data[i] * 4].rgbBlue;
			newData[i * 3 + 1] = bmp.colorPalette[bmp.data[i] * 4].rgbGreen;
			newData[i * 3 + 2] = bmp.colorPalette[bmp.data[i] * 4].rgbRed;
		}

		free(bmp.colorPalette);
		free(bmp.data);

		bmp.colorPalette = (RGBQUAD *)nullptr;
		bmp.data = (uint8_t *)malloc(bmp.infoHeader.biWidth * bmp.infoHeader.biHeight * 3 * sizeof(uint8_t));
		memcpy(bmp.data, newData, bmp.infoHeader.biWidth * bmp.infoHeader.biHeight * 3 * sizeof(uint8_t));

		free(newData);
	}

	// Flip
	if (isFlipped)
	{
		if (bmp.infoHeader.biBitCount == 32)
		{ // 32bits
			img_flipped_JY(bmp.data, *dstWidth, *dstHeight, 4);
		}
		else
		{ // 24bits and 8bits
			img_flipped_JY(bmp.data, *dstWidth, *dstHeight, 3);
		}
	}

	// init dstImage mem
	switch (readTo)
	{
	case GRAY:
		imageInit(dstImage, bmp.infoHeader.biWidth, bmp.infoHeader.biHeight, 1);
		break;
	case RGB:
	case BGR:
		imageInit(dstImage, bmp.infoHeader.biWidth, bmp.infoHeader.biHeight, 3);
		break;
	case RGBA:
	case BGRA:
	case ARGB:
	case ABGR:
		imageInit(dstImage, bmp.infoHeader.biWidth, bmp.infoHeader.biHeight, 4);
		break;
	default:
		// TODO
		printf("UNIMPLEMENTED!!");
		return BMPVIEW_UNIMPLEMENTED_ERROR;
		break;
	}

	// Write dsstImage
	BMP_to_Stream(*dstImage, &bmp, readTo);

	free(bmp.colorPalette);
	free(bmp.data);
	return BMPVIEW_SUCESS;
}

int BGR_to_GrayEC078KH3(uint8_t *outStream, uint8_t *inStream, int width, int height)
{
	int B, G, R;
	int product;
	uint8_t *line, *p;

	static const int gColorMatrix[] = {-2, -2, 7, -2, -2};
	int c = 3;

	line = inStream; //+ (height -1) * width * 3;
	for (int y = 0; y < height; y++)
	{
		p = line;
		for (int x = c; x < width + c; x++)
		{
			int color_idx = (x % 6) / 2;

			B = *(p)*gColorMatrix[color_idx + 0];
			G = *(p + 1) * gColorMatrix[color_idx + 1];
			R = *(p + 2) * gColorMatrix[color_idx + 2];
			// product = (R + G + B) / 3;
			product = (R + G + B) / 3;
			product = product > 255 ? 255 : (product < 0 ? 0 : product);

			*(outStream + width * y + (x - c)) = product;

			p += 3;
		}
		line += width * 3;
		c = 3 - c;
	}

	return 0;
}

int Stream_to_BMP(BMP *bmp, uint8_t *dateStream, ColorTpye streamType)
{
	switch (streamType)
	{
	case GRAY:
		GRAY_to_BGR(bmp->data, dateStream, bmp->infoHeader.biWidth, bmp->infoHeader.biHeight);
		break;
	case RGB:
		RGB_to_BGR(bmp->data, dateStream, bmp->infoHeader.biWidth, bmp->infoHeader.biHeight);
		break;
	case BGR:
		memcpy(bmp->data, dateStream, bmp->infoHeader.biWidth * bmp->infoHeader.biHeight * 3 * sizeof(uint8_t));
		break;
	case RGBA:
		RGBA_to_BGR(bmp->data, dateStream, bmp->infoHeader.biWidth, bmp->infoHeader.biHeight);
		break;
	case BGRA:
		BGRA_to_BGR(bmp->data, dateStream, bmp->infoHeader.biWidth, bmp->infoHeader.biHeight);
		break;
	case R_ONLY:
		GrayR_to_BGR(bmp->data, dateStream, bmp->infoHeader.biWidth, bmp->infoHeader.biHeight);
		break;
	case G_ONLY:
		GrayG_to_BGR(bmp->data, dateStream, bmp->infoHeader.biWidth, bmp->infoHeader.biHeight);
		break;
	case B_ONLY:
		GrayB_to_BGR(bmp->data, dateStream, bmp->infoHeader.biWidth, bmp->infoHeader.biHeight);
		break;
	default:
		// TODO
		printf("unfinished!!");
		return BMPVIEW_UNIMPLEMENTED_ERROR;
		break;
	}

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg saveBMP_JY(uint8_t *srcImage, int width, int height, const char *file_name, ColorTpye streamType, bool isFlipped)
{
	// Bitmap Init
	BMP bmp = new_BMP();
	BMP_init(&bmp, width, height);

	// Stream to bmp
	Stream_to_BMP(&bmp, srcImage, streamType);

	// Flipped
	if (isFlipped)
	{
		img_flipped_JY(bmp.data, bmp.infoHeader.biWidth, bmp.infoHeader.biHeight, 3);
	}

	// save
	// bmp.save(file_name);
	BMP_save(&bmp, file_name);

	free(bmp.colorPalette);
	free(bmp.data);
	return BMPVIEW_SUCESS;
}

int Stream_to_BMP_convert_Panel(BMP *bmp, uint8_t *dateStream, CFA_MASK cfaMask)
{
	int x, y;
	int width = bmp->infoHeader.biWidth;
	int height = bmp->infoHeader.biHeight;
	int cfa_index, cfa_row_index = 0;
	for (y = height - 1; y >= 0; y--)
	{
		for (x = 0; x < width; x++)
		{
			cfa_index = (cfa_row_index % cfaMask.height) * cfaMask.width + (x % cfaMask.width);
			switch (cfaMask.buffer[cfa_index])
			{
			case 0: // R
				*(bmp->data + (y * width + x) * 3 + 2) = *(dateStream + (y * width + x)) >> 4 << 4;
				break;
			case 1: // G
				*(bmp->data + (y * width + x) * 3 + 1) = *(dateStream + (y * width + x)) >> 4 << 4;
				break;
			case 2: // B
				*(bmp->data + (y * width + x) * 3 + 0) = *(dateStream + (y * width + x)) >> 4 << 4;
				break;
			}
		}
		cfa_row_index++;
	}

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg saveBMP_DemoKaleido_JY(uint8_t *srcImage, int width, int height, const char *file_name, CFA_MASK cfaMask, bool isFlipped)
{
	// copy src image
	uint8_t *processImg;
	imageInit(&processImg, width, height, 1);
	memcpy(processImg, srcImage, width * height * sizeof(uint8_t));

	// Flipped
	if (isFlipped)
	{
		img_flipped_JY(processImg, width, height, 1);
	}

	// BMP
	BMP bmp = new_BMP();
	// bmp.init(width, height);
	BMP_init(&bmp, width, height);

	// Steaam to bmp
	Stream_to_BMP_convert_Panel(&bmp, processImg, cfaMask);

	// save bmp
	// bmp.save(file_name);
	BMP_save(&bmp, file_name);

	free(processImg);

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg img_resize_JY(uint8_t **image, int *oldWidth, int *oldHeight, int newWidth, int newHeight, int channel)
{
	/*Linear resize*/

	uint8_t *newImage = (uint8_t *)malloc(newWidth * newHeight * channel * sizeof(uint8_t));
	if (newImage == NULL)
	{
		printf("malloc newImage error\n");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}

	double scaleWidth = (double)newWidth / (double)*oldWidth;
	double scaleHeight = (double)newHeight / (double)*oldHeight;

	int x, y, ch;
	int pixel, nearestMatch;
	for (y = 0; y < newHeight; y++)
	{
		for (x = 0; x < newWidth; x++)
		{
			for (ch = 0; ch < channel; ch++)
			{
				pixel = (y * newWidth + x) * channel + ch;
				nearestMatch = (((int)(y / scaleHeight) * (*oldWidth)) + (int)(x / scaleWidth)) * channel + ch;

				*(newImage + pixel) = *(*image + nearestMatch);
			}
		}
	}

	free(*image);
	*image = (uint8_t *)malloc(newWidth * newHeight * channel * sizeof(uint8_t));
	if (*image == NULL)
	{
		printf("malloc *image error\n");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}

	memcpy(*image, newImage, newWidth * newHeight * channel * sizeof(uint8_t));
	*oldWidth = newWidth;
	*oldHeight = newHeight;

	free(newImage);

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg img_rotation90_JY(uint8_t *image, int *width, int *height, int channel)
{
	int x, y, c;
	uint8_t *rowImg = (uint8_t *)malloc(sizeof(uint8_t) * *width * *height * channel);
	memcpy(rowImg, image, sizeof(uint8_t) * *width * *height * channel);
	int rowWidth = *width;
	int rowHeight = *height;
	*width = rowHeight;
	*height = rowWidth;
	// for (y = 0; y < rowHeight; y++) {
	//	for (x = 0; x < rowWidth; x++) {
	//		for (c = 0; c < channel; c++) {
	//			image[(x * rowHeight + (rowHeight - y - 1)) * channel + c] = rowImg[(y * rowWidth + x) * channel + c];
	//		}
	//	}
	// }
	for (y = 0; y < *height; y++)
	{
		for (x = 0; x < *width; x++)
		{
			for (c = 0; c < channel; c++)
			{
				image[(y * *width + x) * channel + c] = rowImg[((*width - 1 - x) * *height + y) * channel + c];
			}
		}
	}
	free(rowImg);
	return BMPVIEW_SUCESS;
}
bmpViewErrMsg img_rotation180_JY(uint8_t *image, int *width, int *height, int channel)
{
	int x, y, c;
	uint8_t *rowImg = (uint8_t *)malloc(sizeof(uint8_t) * *width * *height * channel);
	memcpy(rowImg, image, sizeof(uint8_t) * *width * *height * channel);
	int rowWidth = *width;
	int rowHeight = *height;
	for (y = 0; y < rowHeight; y++)
	{
		for (x = 0; x < rowWidth; x++)
		{
			for (c = 0; c < channel; c++)
			{
				// image[((rowHeight - y - 1) * rowWidth + (rowWidth - x - 1)) * channel + c] = rowImg[(y * rowWidth + x) * channel + c];
				image[(y * *width + x) * channel + c] = rowImg[((rowHeight - 1 - y) * rowWidth + (rowWidth - 1 - x)) * channel + c];
			}
		}
	}
	free(rowImg);
	return BMPVIEW_SUCESS;
}
bmpViewErrMsg img_rotation270_JY(uint8_t *image, int *width, int *height, int channel)
{
	int x, y, c;
	uint8_t *rowImg = (uint8_t *)malloc(sizeof(uint8_t) * *width * *height * channel);
	memcpy(rowImg, image, sizeof(uint8_t) * *width * *height * channel);
	int rowWidth = *width;
	int rowHeight = *height;
	*width = rowHeight;
	*height = rowWidth;
	// for (y = 0; y < rowHeight; y++) {
	//	for (x = 0; x < rowWidth; x++) {
	//		for (c = 0; c < channel; c++) {
	//			image[((rowWidth - x - 1) * rowHeight + y) * channel + c] = rowImg[(y * rowWidth + x) * channel + c];
	//		}
	//	}
	// }
	for (y = 0; y < *height; y++)
	{
		for (x = 0; x < *width; x++)
		{
			for (c = 0; c < channel; c++)
			{
				image[(y * *width + x) * channel + c] = rowImg[((x * *height) + *height - 1 - y) * channel + c];
			}
		}
	}

	free(rowImg);
	return BMPVIEW_SUCESS;
}

bmpViewErrMsg img_flipped_JY(uint8_t *img, int width, int height, int channel)
{
	uint8_t *newimg = (uint8_t *)malloc(width * height * channel * sizeof(uint8_t));
	if (newimg == NULL)
	{
		printf("malloc Error!!");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}

	int y;
	uint8_t *line = img + (height - 1) * width * channel;

	for (y = 0; y < height; y++)
	{
		memcpy(newimg + y * width * channel, line, width * channel * sizeof(uint8_t));
		line -= width * channel;
	}

	memcpy(img, newimg, width * height * channel * sizeof(uint8_t));

	free(newimg);

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg img_mirror_JY(uint8_t *img, int width, int height, int channel)
{
	uint8_t *newimg = (uint8_t *)malloc(width * height * channel * sizeof(uint8_t));
	if (newimg == NULL)
	{
		printf("malloc Error!!");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}

	int x, y, c;
	for (y = 0; y < height; y++)
	{
		for (x = 0; x < width; x++)
		{
			for (c = 0; c < channel; c++)
			{
				newimg[(y * width + (width - x - 1)) * channel + c] = img[(y * width + x) * channel + c];
			}
		}
	}

	memcpy(img, newimg, width * height * channel * sizeof(uint8_t));

	free(newimg);

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg img_crop_JY(uint8_t *image, int width, int height, int channel, uint8_t **cropImg, int *cropWidth, int *cropHeight, int left, int top, int right, int bottom)
{
	if (left < 0 || top < 0 || right > width || bottom > height)
	{
		return BMPVIEW_ARGUMENTS_ERROR;
	}

	int resWidth = right - left;
	int resHeight = bottom - top;

	uint8_t *resImg = (uint8_t *)malloc(resWidth * resHeight * channel * sizeof(uint8_t));
	if (resImg == NULL)
	{
		printf("malloc newImage error\n");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}

	for (int y = top; y < bottom; y++)
	{
		for (int x = left; x < right; x++)
		{
			memcpy(resImg + (((y - top) * resWidth + (x - left)) * channel), image + (y * width + x) * channel, channel * sizeof(uint8_t));
			// for (int c = 0; c < channel; c++) {
			//	resImg[((y - top) * resWidth + x) * channel + c] = image[(y * width + x) * channel + c];
			// }
		}
	}

	*cropWidth = resWidth;
	*cropHeight = resHeight;
	*cropImg = (uint8_t *)malloc(*cropWidth * *cropHeight * channel * sizeof(uint8_t));
	if (*cropImg == NULL)
	{
		printf("malloc newImage error\n");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}
	memcpy(*cropImg, resImg, *cropWidth * *cropHeight * channel * sizeof(uint8_t));

	free(resImg);
	return BMPVIEW_SUCESS;
}

bmpViewErrMsg img_padding_JY(uint8_t **image, int *width, int *height, int channel, int left_padding, int top_padding, int right_padding, int bottom_padding)
{

	uint8_t *tmpImg = (uint8_t *)malloc(*width * *height * channel * sizeof(uint8_t));
	memcpy(tmpImg, *image, *width * *height * channel * sizeof(uint8_t));

	int resWidth = *width + left_padding + right_padding;
	int resHeight = *height + top_padding + bottom_padding;

	free(*image);
	*image = (uint8_t *)malloc(resWidth * resHeight * channel * sizeof(uint8_t));
	memset(*image, 0, resWidth * resHeight * channel * sizeof(uint8_t));

	for (int y = 0; y < *height; y++)
	{
		for (int x = 0; x < *width; x++)
		{
			for (int c = 0; c < channel; c++)
			{
				(*image)[((y + top_padding) * resWidth + (x + left_padding)) * channel + c] = tmpImg[(y * *width + x) * channel + c];
			}
		}
	}

	*width = resWidth;
	*height = resHeight;
	free(tmpImg);

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg readPPM_JY(uint8_t **dstImage, int *width, int *height, const char *file_name)
{
	uint8_t thisByte;
	uint8_t buff[5];
	uint8_t *pBuff = buff;

	int rgb_comp_color;

	FILE *fp_s = NULL;
	int res_fread;
	fp_s = fopen(file_name, "rb");
	if (fp_s == NULL)
	{
		printf("Error opening file!\n");
		return BMPVIEW_FAILED_OPEN_FILE;
	}

	// read format
	pBuff = buff;
	memset(buff, 0, sizeof(buff) / sizeof(buff[0]));
	while (true)
	{
		res_fread = fread(&thisByte, sizeof(uint8_t), 1, fp_s);
		if (isalnum(thisByte))
			*pBuff++ = thisByte;
		else
			break;
	}
	LOG("format: %s\n", buff);
	if (buff[0] != 'P' || buff[1] != '6')
	{
		printf("Invalid image format (must be 'P6')\n");
		return BMPVIEW_INVALID_IMAGE_FORMAT;
	}

	// read width
	pBuff = buff;
	memset(buff, 0, sizeof(buff) / sizeof(buff[0]));
	while (true)
	{
		res_fread = fread(&thisByte, sizeof(uint8_t), 1, fp_s);
		if (isalnum(thisByte))
			*pBuff++ = thisByte;
		else
			break;
	}
	sscanf((const char *)buff, "%d", width);
	LOG("width: %d\n", *width);

	// read height
	pBuff = buff;
	memset(buff, 0, sizeof(buff) / sizeof(buff[0]));
	while (true)
	{
		res_fread = fread(&thisByte, sizeof(uint8_t), 1, fp_s);
		if (isalnum(thisByte))
			*pBuff++ = thisByte;
		else
			break;
	}
	sscanf((const char *)buff, "%d", height);
	LOG("height: %d\n", *height);

	// read rgb_comp_color
	pBuff = buff;
	memset(buff, 0, sizeof(buff) / sizeof(buff[0]));
	while (true)
	{
		res_fread = fread(&thisByte, sizeof(uint8_t), 1, fp_s);
		if (isalnum(thisByte))
			*pBuff++ = thisByte;
		else
			break;
	}
	sscanf((const char *)buff, "%d", &rgb_comp_color);
	LOG("rgb_comp_color: %d\n", rgb_comp_color);
	if (rgb_comp_color != 255)
	{
		printf("'%s' does not have 8-bits components\n", file_name);
		return BMPVIEW_OTHER_ERROR;
	}

	// dstImage allocate memory
	*(uint8_t **)dstImage = (uint8_t *)malloc(sizeof(uint8_t) * *width * *height * 3);
	if (*(uint8_t **)dstImage == NULL)
	{
		printf("imageInit malloc Error!!\n");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}

	// read data
	res_fread = fread(*dstImage, sizeof(uint8_t) * (*width) * (*height) * 3, 1, fp_s);

	if (res_fread)
	{
		// todo
	}

	fclose(fp_s);
	return BMPVIEW_SUCESS;
}
bmpViewErrMsg savePPM_JY(uint8_t *srcImage, int width, int height, const char *file_name)
{

	FILE *fp_t = NULL;
	fp_t = fopen(file_name, "wb");
	if (fp_t == NULL)
	{
		printf("Error opening file!\n");
		return BMPVIEW_FAILED_OPEN_FILE;
	}

	// write fileHeader
	char header[127];
	sprintf(header, "P6\n%d %d\n255\n", width, height);
	fseek(fp_t, 0, SEEK_SET);
	fwrite(&header, strlen(header), 1, fp_t);

	// write data
	fwrite(srcImage, width * height * 3, 1, fp_t);

	fclose(fp_t);

	return BMPVIEW_SUCESS;
}
bmpViewErrMsg readPGM_JY(uint8_t **dstImage, int *width, int *height, const char *file_name)
{
	uint8_t thisByte;
	uint8_t buff[5];
	uint8_t *pBuff = buff;

	int rgb_comp_color;

	FILE *fp_s = NULL;
	int res_fread;
	fp_s = fopen(file_name, "rb");
	if (fp_s == NULL)
	{
		printf("Error opening file!\n");
		return BMPVIEW_FAILED_OPEN_FILE;
	}

	// read format
	pBuff = buff;
	memset(buff, 0, sizeof(buff) / sizeof(buff[0]));
	while (true)
	{
		res_fread = fread(&thisByte, sizeof(uint8_t), 1, fp_s);
		if (isalnum(thisByte))
			*pBuff++ = thisByte;
		else
			break;
	}
	LOG("format: %s\n", buff);
	if (buff[0] != 'P' || buff[1] != '5')
	{
		printf("Invalid image format (must be 'P5')\n");
		return BMPVIEW_INVALID_IMAGE_FORMAT;
	}

	// read width
	pBuff = buff;
	memset(buff, 0, sizeof(buff) / sizeof(buff[0]));
	while (true)
	{
		res_fread = fread(&thisByte, sizeof(uint8_t), 1, fp_s);
		if (isalnum(thisByte))
			*pBuff++ = thisByte;
		else
			break;
	}
	sscanf((const char *)buff, "%d", width);
	LOG("width: %d\n", *width);

	// read height
	pBuff = buff;
	memset(buff, 0, sizeof(buff) / sizeof(buff[0]));
	while (true)
	{
		res_fread = fread(&thisByte, sizeof(uint8_t), 1, fp_s);
		if (isalnum(thisByte))
			*pBuff++ = thisByte;
		else
			break;
	}
	sscanf((const char *)buff, "%d", height);
	LOG("height: %d\n", *height);

	// read rgb_comp_color
	pBuff = buff;
	memset(buff, 0, sizeof(buff) / sizeof(buff[0]));
	while (true)
	{
		res_fread = fread(&thisByte, sizeof(uint8_t), 1, fp_s);
		if (isalnum(thisByte))
			*pBuff++ = thisByte;
		else
			break;
	}
	sscanf((const char *)buff, "%d", &rgb_comp_color);
	LOG("rgb_comp_color: %d\n", rgb_comp_color);
	if (rgb_comp_color != 255)
	{
		printf("'%s' does not have 8-bits components\n", file_name);
		return BMPVIEW_OTHER_ERROR;
	}

	// dstImage allocate memory
	*(uint8_t **)dstImage = (uint8_t *)malloc(sizeof(uint8_t) * *width * *height);
	if (*(uint8_t **)dstImage == NULL)
	{
		printf("imageInit malloc Error!!\n");
		return BMPVIEW_FAILED_MEMORY_ALLOCATION;
	}

	// read data
	res_fread = fread(*dstImage, sizeof(uint8_t) * (*width) * (*height), 1, fp_s);

	if (res_fread)
	{
		// todo
	}

	fclose(fp_s);
	return BMPVIEW_SUCESS;
}
bmpViewErrMsg savePGM_JY(uint8_t *srcImage, int width, int height, const char *file_name)
{

	FILE *fp_t = NULL;
	fp_t = fopen(file_name, "wb");
	if (fp_t == NULL)
	{
		printf("Error opening file!\n");
		return BMPVIEW_FAILED_OPEN_FILE;
	}

	// write fileHeader
	char header[127];
	sprintf(header, "P5\n%d %d\n255\n", width, height);
	fseek(fp_t, 0, SEEK_SET);
	fwrite(&header, strlen(header), 1, fp_t);

	// write data
	fwrite(srcImage, width * height, 1, fp_t);

	fclose(fp_t);

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg savePPM_DemoKaleido_JY(uint8_t *srcImage, int width, int height, const char *file_name, CFA_MASK cfaMask)
{

	uint8_t *saveImg = (uint8_t *)malloc(width * height * 3 * sizeof(uint8_t));
	memset(saveImg, 0, width * height * 3 * sizeof(uint8_t));
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			int cfa_index = (y % cfaMask.height) * cfaMask.width + (x % cfaMask.width);
			switch (cfaMask.buffer[cfa_index])
			{
			case 0: // R
				saveImg[(y * width + x) * 3 + 0] = srcImage[y * width + x] >> 4 << 4;
				break;
			case 1: // G
				saveImg[(y * width + x) * 3 + 1] = srcImage[y * width + x] >> 4 << 4;
				break;
			case 2: // B
				saveImg[(y * width + x) * 3 + 2] = srcImage[y * width + x] >> 4 << 4;
				break;
			}
		}
	}

	savePPM_JY(saveImg, width, height, file_name);
	free(saveImg);

	return BMPVIEW_SUCESS;
}

bmpViewErrMsg saveBMP_DemoACeP2_JY(uint8_t *srcImage, int width, int height, const char *file_name)
{
	// copy src image
	uint8_t *processImg;
	imageInit(&processImg, width, height, 1);
	memcpy(processImg, srcImage, width * height * sizeof(uint8_t));

	// Flipped
	if (true)
	{
		img_flipped_JY(processImg, width, height, 1);
	}

	// BMP
	BMP bmp = new_BMP();
	// bmp.init(width, height);
	BMP_init(&bmp, width, height);

	// Steaam to bmp
	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			switch (processImg[y * width + x])
			{
			case 0 << 3:
			case 1 << 3:
			case 2 << 3:
			case 3 << 3:
				// K
				bmp.data[(y * width + x) * 3 + 0] = 0;
				bmp.data[(y * width + x) * 3 + 1] = 0;
				bmp.data[(y * width + x) * 3 + 2] = 0;
				break;

			case 4 << 3:
			case 5 << 3:
			case 6 << 3:
				// R
				bmp.data[(y * width + x) * 3 + 0] = 0;
				bmp.data[(y * width + x) * 3 + 1] = 0;
				bmp.data[(y * width + x) * 3 + 2] = 255;
				break;

			case 8 << 3:
			case 9 << 3:
			case 10 << 3:
				// G
				bmp.data[(y * width + x) * 3 + 0] = 0;
				bmp.data[(y * width + x) * 3 + 1] = 255;
				bmp.data[(y * width + x) * 3 + 2] = 0;
				break;

			case 12 << 3:
			case 13 << 3:
			case 14 << 3:
				// B
				bmp.data[(y * width + x) * 3 + 0] = 255;
				bmp.data[(y * width + x) * 3 + 1] = 0;
				bmp.data[(y * width + x) * 3 + 2] = 0;
				break;

			case 16 << 3:
			case 17 << 3:
			case 18 << 3:
				// C
				bmp.data[(y * width + x) * 3 + 0] = 255;
				bmp.data[(y * width + x) * 3 + 1] = 255;
				bmp.data[(y * width + x) * 3 + 2] = 0;
				break;

			case 20 << 3:
			case 21 << 3:
			case 22 << 3:
				// M
				bmp.data[(y * width + x) * 3 + 0] = 255;
				bmp.data[(y * width + x) * 3 + 1] = 0;
				bmp.data[(y * width + x) * 3 + 2] = 255;
				break;

			case 24 << 3:
			case 25 << 3:
			case 26 << 3:
				// Y
				bmp.data[(y * width + x) * 3 + 0] = 0;
				bmp.data[(y * width + x) * 3 + 1] = 255;
				bmp.data[(y * width + x) * 3 + 2] = 255;
				break;

			case 28 << 3:
			case 29 << 3:
			case 30 << 3:
			case 31 << 3:
				// W
				bmp.data[(y * width + x) * 3 + 0] = 255;
				bmp.data[(y * width + x) * 3 + 1] = 255;
				bmp.data[(y * width + x) * 3 + 2] = 255;
				break;
			}
		}
	}

	// save bmp
	// bmp.save(file_name);
	BMP_save(&bmp, file_name);

	free(processImg);

	return BMPVIEW_SUCESS;
}
